package hos.location;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import hos.map.coordinate.CoordinateType;
import hos.map.coordinate.LatLng;
import hos.map.distance.BearingDistanceCache;
import hos.map.distance.LengthUnit;

/**
 * <p>Title: LocationData </p>
 * <p>Description:  </p>
 * <p>Company: www.mapuni.com </p>
 *
 * @author : 蔡俊峰
 * @version : 1.0
 * @date : 2023-12-10 21:14
 */
public class LocationData {
    /**
     * 点位
     */
    public final LatLng latLng;
    /**
     * 速度
     */
    public final float speed;
    /**
     * 方向
     */
    public final float direction;
    /**
     * 准确度
     */
    public final float accuracy;
    /**
     * 海拔高度
     */
    public final double altitude;
    /**
     * 卫星数量
     */
    public final int satellitesNum;
    /**
     * gps network
     */
    public final String provider;

    public double header;

    public final long time;

    /**
     * 地址
     */
    public final List<String> address;
    /**
     * 城市
     */
    public final String city;
    /**
     * 城市编码
     */
    public final String cityCode;
    /**
     * 国家
     */
    public final String country;
    /**
     * 省份
     */
    public final String province;
    /**
     * 街道
     */
    public final String thoroughfare;

    public LocationData(String provider, LatLng latLng, float speed, float direction,
                        float accuracy, double altitude, int satellitesNum, long time,
                        double header, List<String> address, String city, String cityCode, String country, String province,
                        String thoroughfare) {
        this.latLng = latLng;
        this.speed = speed;
        this.direction = direction;
        this.accuracy = accuracy;
        this.altitude = altitude;
        this.satellitesNum = satellitesNum;
        this.provider = provider;
        this.header = header;
        this.time = time;
        this.address = address;
        this.city = city;
        this.cityCode = cityCode;
        this.country = country;
        this.province = province;
        this.thoroughfare = thoroughfare;
    }

    protected LocationData(String provider, LatLng latLng, float speed, float direction,
                           float accuracy, double altitude, int satellitesNum, long time,
                           double header) {
        this(provider, latLng, speed, direction, accuracy, altitude, satellitesNum, time, header,
                null, null, null, null, null, null);
    }

    public CoordinateType getType() {
        return latLng.getType();
    }

    /**
     * 判断经纬度是否相同
     *
     * @param locationData 目标经纬度
     */
    public boolean isRepeated(LocationData locationData) {
        return this.latLng.isRepeated(locationData.latLng);
    }

    /**
     * 判断经纬度是否相同
     *
     * @param locationData 目标经纬度
     */
    public boolean isCircleRepeated(LocationData locationData) {
        return isCircleRepeated(locationData, 20);
    }

    /**
     * 判断经纬度是否在圆内
     *
     * @param locationData 目标定位
     * @param radius       半径
     */
    public boolean isCircleRepeated(LocationData locationData, int radius) {
        return this.latLng.isCircleRepeated(locationData.latLng, radius);
    }

    protected static boolean isEmpty(Map<String, Object> json, String key) {
        Object value = json.get(key);
        return value == null || value.toString().isEmpty();
    }

    protected static double emptyDouble(Map<String, Object> json, String key) {
        Object value = json.get(key);
        if (value == null || value.toString().isEmpty()) {
            return 0;
        }
        return (double) value;
    }

    protected static float emptyFloat(Map<String, Object> json, String key) {
        Object value = json.get(key);
        if (value == null || value.toString().isEmpty()) {
            return 0;
        }
        return (float) value;
    }

    protected static int emptyInteger(Map<String, Object> json, String key) {
        Object value = json.get(key);
        if (value == null || value.toString().isEmpty()) {
            return 0;
        }
        return (int) value;
    }

    protected static long emptyLong(Map<String, Object> json, String key) {
        Object value = json.get(key);
        if (value == null || value.toString().isEmpty()) {
            return 0;
        }
        return (long) value;
    }

    /**
     * 从map中生成LocationData
     */
    public static LocationData fromJson(Map<String, Object> json) {
        if (isEmpty(json, "latitude")) throw new NullPointerException("latitude is null");
        if (isEmpty(json, "longitude")) throw new NullPointerException("longitude is null");
        String typeName = (String) json.get("type");
        String provider = (String) json.get("provider");
        Object address = json.get("address");
        List<String> addressList = null;
        if (address instanceof List) {
            addressList = (List<String>) address;
        }

        CoordinateType form = CoordinateType.form(typeName);
        int satellitesNum = emptyInteger(json, "satellitesNum");
        double latitude = emptyDouble(json, "latitude");
        double longitude = emptyDouble(json, "longitude");
        double altitude = emptyDouble(json, "altitude");
        float accuracy = emptyFloat(json, "accuracy");
        float speed = emptyFloat(json, "speed");
        float direction = emptyFloat(json, "direction");
        if (direction == 0.0) {
            direction = emptyFloat(json, "header");
        }
        return new Builder(new LatLng(latitude, longitude, form))
                .accuracy(accuracy)
                .altitude(altitude)
                .provider(provider)
                .speed(speed)
                .address(addressList)
                .direction(direction)
                .header(emptyFloat(json, "header"))
                .time(emptyLong(json, "time"))
                .satellitesNum(satellitesNum)
                .cityCode((String) json.get("cityCode"))
                .city((String) json.get("city"))
                .country((String) json.get("country"))
                .province((String) json.get("province"))
                .thoroughfare((String) json.get("thoroughfare"))
                .build();
    }

    public String toJson() {
//        StringBuilder builder = new StringBuilder();
//        builder.append("{");
//
//        StringBuilderJsonUtils.appendStringField(builder, "type", latLng.getType().getName());
//        StringBuilderJsonUtils.appendStringField(builder, "provider", provider);
//        StringBuilderJsonUtils.appendStringField(builder, "id", latLng.getId());
//        StringBuilderJsonUtils.appendStringField(builder, "json", latLng.getJson());
//        StringBuilderJsonUtils.appendObjectField(builder, "latitude", latLng.getLatitude());
//        StringBuilderJsonUtils.appendObjectField(builder, "longitude", latLng.getLongitude());
//        StringBuilderJsonUtils.appendObjectField(builder, "accuracy", accuracy);
//        StringBuilderJsonUtils.appendObjectField(builder, "altitude", altitude);
//        StringBuilderJsonUtils.appendObjectField(builder, "direction", direction);
//
//        // address 特殊处理
//        if (address != null) {
//            StringBuilder addressBuilder = new StringBuilder();
//            for (String addressStr : address) {
//                if (addressBuilder.length() != 0) {
//                    addressBuilder.append(",");
//                }
//                addressBuilder.append(addressStr);
//            }
//            StringBuilderJsonUtils.appendStringField(builder, "address", addressBuilder.toString());
//        } else {
//            StringBuilderJsonUtils.appendNullField(builder, "address");
//        }
//
//        StringBuilderJsonUtils.appendStringField(builder, "city", city);
//        StringBuilderJsonUtils.appendStringField(builder, "cityCode", cityCode);
//        StringBuilderJsonUtils.appendStringField(builder, "province", province);
//        StringBuilderJsonUtils.appendStringField(builder, "thoroughfare", thoroughfare);
//        StringBuilderJsonUtils.appendStringField(builder, "country", country);
//        StringBuilderJsonUtils.appendObjectField(builder, "speed", speed);
//        StringBuilderJsonUtils.appendObjectField(builder, "satellitesNum", satellitesNum);
//        StringBuilderJsonUtils.appendObjectField(builder, "time", time);
//        StringBuilderJsonUtils.appendObjectField(builder, "header", header);
//
//        builder.setLength(builder.length() - 1); // 移除最后一个逗号
//        builder.append("}");

        return JsonUtils.toJson(toMap());
    }


    public Map<String, Object> toMap() {
    // 避免频繁扩容
    Map<String, Object> map = new HashMap<>();

    if (latLng != null) {
        map.put("type", latLng.getType() != null ? latLng.getType().getName() : null);
        map.put("id", latLng.getId());
        map.put("json", latLng.getJson());
        map.put("latitude", latLng.getLatitude());
        map.put("longitude", latLng.getLongitude());
    } else {
        map.put("type", null);
        map.put("id", null);
        map.put("json", null);
        map.put("latitude", null);
        map.put("longitude", null);
    }

    map.put("provider", provider);
    map.put("accuracy", accuracy);
    map.put("altitude", altitude);
    map.put("direction", direction);
    map.put("address", address);
    map.put("speed", speed);
    map.put("satellitesNum", satellitesNum);
    map.put("time", time);
    map.put("header", header);
    map.put("cityCode", cityCode);
    map.put("city", city);
    map.put("country", country);
    map.put("thoroughfare", thoroughfare);
    map.put("province", province);

    return map;
}


    public BearingDistanceCache distance(LocationData target) {
        return latLng.distance(target.latLng);
    }

    public LocationData offset(double distanceInMeter, double bearing) {
        return new Builder(latLng.offset(distanceInMeter, bearing))
                .provider(provider)
                .latitude(latLng.getLatitude())
                .longitude(latLng.getLongitude())
                .speed(speed)
                .direction(direction)
                .accuracy(accuracy)
                .altitude(altitude)
                .satellitesNum(satellitesNum)
                .time(time)
                .header(header)
                .city(city)
                .cityCode(cityCode)
                .country(country)
                .province(province)
                .thoroughfare(thoroughfare)
                .address(address)
                .build();
    }

    public BearingDistanceCache distance(LengthUnit unit, LocationData target) {
        return latLng.distance(unit, target.latLng);
    }

    public double bearing(LocationData target) {
        return latLng.bearing(target.latLng);
    }

    public Builder newBuilder() {
        return new Builder(latLng)
                .provider(provider)
                .speed(speed)
                .direction(direction)
                .accuracy(accuracy)
                .altitude(altitude)
                .satellitesNum(satellitesNum)
                .time(time)
                .header(header)
                .city(city)
                .cityCode(cityCode)
                .country(country)
                .province(province)
                .thoroughfare(thoroughfare)
                .address(address);
    }

    public static class Builder {
        private double latitude;
        private double longitude;
        private float speed;
        private float direction;
        private float accuracy;
        private double altitude;
        private int satellitesNum;
        private long time;
        private double header;
        private final CoordinateType type;
        private LatLng latLng;
        private String provider;


        /**
         * 地址
         */
        private List<String> address;
        /**
         * 城市
         */
        private String city;
        /**
         * 城市编码
         */
        private String cityCode;
        /**
         * 国家
         */
        private String country;
        /**
         * 省份
         */
        private String province;

        public String thoroughfare;

        public Builder(CoordinateType type) {
            this.type = type;
        }

        public Builder(LatLng latLng) {
            this.type = latLng.getType();
            this.latLng = latLng;
            this.latitude = latLng.getLatitude();
            this.longitude = latLng.getLongitude();
        }

        public Builder provider(String provider) {
            this.provider = provider;
            return this;
        }

        public Builder latitude(double latitude) {
            this.latitude = latitude;
            return this;
        }

        public Builder longitude(double longitude) {
            this.longitude = longitude;
            return this;
        }

        public Builder speed(float speed) {
            this.speed = speed;
            return this;
        }

        public Builder direction(float direction) {
            this.direction = direction;
            return this;
        }

        public Builder accuracy(float accuracy) {
            this.accuracy = accuracy;
            return this;
        }

        public Builder altitude(double altitude) {
            this.altitude = altitude;
            return this;
        }

        public Builder satellitesNum(int satellitesNum) {
            this.satellitesNum = satellitesNum;
            return this;
        }

        public Builder time(long time) {
            this.time = time;
            return this;
        }

        public Builder header(double header) {
            this.header = header;
            return this;
        }

        public Builder address(List<String> address) {
            this.address = address;
            return this;
        }

        public Builder addAddress(String address) {
            if (this.address == null) {
                this.address = new ArrayList<>();
            }
            this.address.add(address);
            return this;
        }

        public Builder city(String city) {
            this.city = city;
            return this;
        }

        public Builder cityCode(String cityCode) {
            this.cityCode = cityCode;
            return this;
        }

        public Builder country(String country) {
            this.country = country;
            return this;
        }

        public Builder province(String province) {
            this.province = province;
            return this;
        }

        public Builder thoroughfare(String thoroughfare) {
            this.thoroughfare = thoroughfare;
            return this;
        }

        public LocationData build() {
            LatLng finalLatLng = latLng != null ? latLng : new LatLng(latitude, longitude, type);
            return new LocationData(provider, finalLatLng,
                    this.speed, direction, accuracy, altitude, satellitesNum, time, header,
                    address, city, cityCode, country, province, thoroughfare);
        }
    }
}
